/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2012-2019. All rights reserved.
 * Description: white list func for driver
 * Author:  huawei
 * Create:  2019-10-21
 */
#include <linux/crypto.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/securec.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/sched/mm.h>

#include "tee/tz_api.h"
#include "drv_whitelist.h"

STATIC process_file_t g_process_file = { 0 };

/*
 * calculate sha256 check code for input data
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
#include <crypto/hash.h>
STATIC s32 whitelist_calculate_sha256(const u8 *data, u32 data_len, u8 *sha256_code, u32 sha256_len)
{
    s32 ret;
    struct crypto_shash *sha256 = NULL;

    if (sha256_len != WL_SHA256_DIGEST) {
        WHITELIST_ERR("sha256 len err(%u).\n", (u32)sha256_len);
        return WL_ERR_PARA;
    }

    ret = memset_s(sha256_code, WL_SHA256_DIGEST, 0, WL_SHA256_DIGEST);
    if (ret) {
        WHITELIST_ERR("memset_s for sha256 code failed, ret[%d].\n", ret);
        return WL_ERR_MEM_INIT;
    }

    sha256 = crypto_alloc_shash("sha256", 0, 0);
    if (IS_ERR_OR_NULL(sha256)) {
        WHITELIST_ERR("crypto_alloc_shash failed.\n");
        return WL_ERR_SHA256_CHECK;
    }

    do {
        SHASH_DESC_ON_STACK(shash, sha256);
        shash->tfm = sha256;

        ret = crypto_shash_init(shash);
        if (ret < 0) {
            WHITELIST_ERR("crypto_shash_init failed. ret: %d\n", ret);
            goto OUT;
        }
        ret = crypto_shash_update(shash, data, data_len);
        if (ret < 0) {
            WHITELIST_ERR("crypto_shash_update failed. ret: %d\n", ret);
            goto OUT;
        }
        ret = crypto_shash_final(shash, sha256_code);
        if (ret < 0) {
            WHITELIST_ERR("crypto_shash_final failed. ret: %d\n", ret);
        }
    } while (0);

OUT:
    crypto_free_shash(sha256);
    return ret;
}
#else
STATIC s32 whitelist_calculate_sha256(const u8 *data, u32 data_len, u8 *sha256_code, u32 sha256_len)
{
    s32 ret;
    struct scatterlist sg;
    struct hash_desc desc;

    if (sha256_len != WL_SHA256_DIGEST) {
        WHITELIST_ERR(" crypto_alloc_hash failed.\n");
        return WL_ERR_PARA;
    }

    ret = memset_s(sha256_code, WL_SHA256_DIGEST, 0, WL_SHA256_DIGEST);
    if (ret) {
        WHITELIST_ERR("memset_s for sha256 code failed, ret[%d].\n", ret);
        return WL_ERR_MEM_INIT;
    }

    desc.flags = 0;
    desc.tfm = crypto_alloc_hash("sha256", 0, 0);

    if (IS_ERR_OR_NULL(desc.tfm)) {
        WHITELIST_ERR(" crypto_alloc_hash failed.\n");
        return WL_ERR_SHA256_CHECK;
    }

    sg_init_one(&sg, data, data_len);

    ret = crypto_hash_init(&desc);
    if (ret != 0) {
        WHITELIST_ERR("crypto_hash_init failed.\n");
        goto OUT;
    }
    ret = crypto_hash_update(&desc, &sg, data_len);
    if (ret != 0) {
        WHITELIST_ERR("crypto_hash_update failed.\n");
        goto OUT;
    }
    ret = crypto_hash_final(&desc, sha256_code);
    if (ret != 0) {
        WHITELIST_ERR("crypto_hash_update failed.\n");
    }
OUT:
    crypto_free_hash(desc.tfm);
    return ret;
}
#endif

/*
 * verify soc for process.list file
 */
STATIC s32 whitelist_file_soc_verification(s32 dev_id, u8 *file_content, u32 file_size)
{
    s32 ret;
    u8 *code_base = NULL;

    ret = verify_init_session(dev_id);
    if (ret != 0) {
        WHITELIST_ERR("dev(%d) verify_init_session fail, ret: %d\n", dev_id, ret);
        return ret;
    }

    code_base = (u8 *)(uintptr_t)(((uintptr_t)file_content) + SOC_HEAD_SIZE);
    if (sec_update_verification(dev_id, (const u8 *)file_content, 0, (const u8 *)code_base, file_size) != 0) {
        verify_finalize_session(dev_id);
        WHITELIST_ERR("dev(%d) sec_update_verification fail.\n", dev_id);
        return WL_ERR_SOC_VERIFY;
    }

    verify_finalize_session(dev_id);
    return WL_OK;
}

/*
 * get current process name and name size
 */
s32 whitelist_get_cur_process_name(u8 **p_file_name, u32 *name_size)
{
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma = NULL;
    s32 is_found = 0;

    if (mm == NULL) {
        WHITELIST_ERR("mm is NULL.\n");
        return WL_ERR_PARA;
    }

    vma = mm->mmap;
    if (vma == NULL) {
        WHITELIST_ERR("vma is NULL.\n");
        return WL_ERR_PARA;
    }

    while (vma != NULL) {
        if ((vma->vm_flags & U8_MAX) == PROCESS_EXCUTABLE_FLAG) {
            if (vma->vm_file == NULL) {
                WHITELIST_ERR("vma->vm_file is NULL.\n");
                return WL_ERR_PARA;
            }

            if (vma->vm_file->f_path.dentry == NULL) {
                WHITELIST_ERR("file path dentry is NULL.\n");
                return WL_ERR_PARA;
            }

            *p_file_name = (u8 *)vma->vm_file->f_path.dentry->d_name.name;
            *name_size = strlen(*p_file_name);
            is_found = 1;
            break;
        }

        vma = vma->vm_next;
    }

    if (!is_found) {
        WHITELIST_ERR("can't get process name by vma.\n");
        return WL_ERR_PARA;
    }

    return WL_OK;
}
EXPORT_SYMBOL(whitelist_get_cur_process_name);

/*
 * get all process info in process list file
 */
STATIC s32 whitelist_get_process_info(u8 *file_content, u32 file_size)
{
    u32 i = 0;
    s32 start_offset = 0;
    u32 end_offset = 0;
    u32 name_len = 0;
    process_parse_info_t *parse_info = NULL;

    if (file_content == NULL) {
        WHITELIST_ERR("file content is NULL.\n");
        return WL_ERR_PARA;
    }

    if ((file_size > PROCESS_LIST_FILE_MAX) || (file_size < PROCESS_LIST_SHA256_BYTES)) {
        WHITELIST_ERR("file size is too short.\n");
        return WL_ERR_PARA;
    }

    while (start_offset < (file_size - PROCESS_LIST_SHA256_BYTES)) {
        /* get process name len */
        for (i = start_offset; i < file_size - BUF_OFFSET_2BYTES; i++) {
            if ((file_content[i] == ';') && (file_content[i + 1] == '\n')) {
                name_len = i - start_offset;
                break;
            }
        }

        /* get process file end position */
        for (i = start_offset; i < file_size - BUF_OFFSET_3BYTES; i++) {
            if ((file_content[i] == ';') && (file_content[i + 1] == '\n') &&
                (file_content[i + BUF_OFFSET_2BYTES] == ';')) {
                end_offset = i + BUF_OFFSET_3BYTES;
                break;
            }
        }

        /* if can't find start position, return failed */
        if (end_offset == 0) {
            WHITELIST_ERR("can't find match end position.\n");
            return WL_ERR_PARA;
        }

        parse_info = (process_parse_info_t *)vzalloc(sizeof(process_parse_info_t));
        if (parse_info == NULL) {
            WHITELIST_ERR("parse info memory alloc failed.\n");
            return WL_ERR_MEM_ALLOC;
        }

        /* init link list for process related file(so or bin) */
        INIT_LIST_HEAD(&parse_info->file_list);

        /* add node to process link list, to record process info */
        list_add(&parse_info->process_list, &g_process_file.process_list);
        parse_info->start_pos = (u8 *)&file_content[start_offset];
        parse_info->content_size = end_offset - start_offset;
        parse_info->process_name = (u8 *)&file_content[start_offset];
        parse_info->process_name_len = name_len;
        start_offset = end_offset + 1;
        end_offset = 0;
    }

    return WL_OK;
}

/*
 * check if current process name is in whitelist name array
 */
STATIC s32 whitelist_check_process_name(const char **list_name, u32 name_num)
{
    s32 ret;
    s32 i = 0;
    u8 *p_file_name = NULL;
    u32 process_name_len = 0;
    u32 wl_name_len = 0;

    ret = whitelist_get_cur_process_name(&p_file_name, &process_name_len);
    if (ret) {
        WHITELIST_ERR("whitelist_get_cur_process_name failed, ret = %d.", ret);
        return WL_ERR_PARA;
    }

    if ((p_file_name == NULL) || (list_name == NULL)) {
        WHITELIST_ERR("cur process file point is NULL.\n");
        return WL_ERR_PARA;
    }

    /* check if current process is in white list */
    for (i = 0; i < name_num; i++) {
        wl_name_len = strlen(list_name[i]);
        if (wl_name_len == process_name_len) {
            ret = memcmp(list_name[i], p_file_name, process_name_len);
            if (!ret) {
                p_file_name = NULL;
                return WL_OK;
            }
        }
    }

    p_file_name = NULL;
    WHITELIST_ERR("process name not in whitelist. \n");
    return WL_ERR_PARA;
}

/*
 * get vma by file path
 */
STATIC struct vm_area_struct *whitelist_get_vma_by_file(const char *file_path, u32 path_len)
{
    s32 ret;
    char *res = NULL;
    int error_flag = 0;
    char *p_file_path = NULL;
    struct vm_area_struct *vma = NULL;
    struct mm_struct *mm = get_task_mm(current);

    if (mm == NULL) {
        WHITELIST_ERR("mm is NULL.\n");
        return NULL;
    }

    p_file_path = (char *)vmalloc(PATH_MAX);
    if (p_file_path == NULL) {
        mmput(mm);
        WHITELIST_ERR("file path vmalloc failed.\n");
        return NULL;
    }

    down_read(&mm->mmap_lock);
    vma = mm->mmap;
    /* inquiry all vma and find the match file path */
    while (vma != NULL) {
        if ((vma->vm_flags & U8_MAX) == PROCESS_EXCUTABLE_FLAG) {
            if (vma->vm_file == NULL) {
                vma = vma->vm_next;
                continue;
            }

            ret = memset_s(p_file_path, PATH_MAX, 0, PATH_MAX);
            if (ret) {
                WHITELIST_ERR("memset_s failed, ret = %d.\n", ret);
                goto FAILED_OUT;
            }

            /* get vma file path and compare to process.list file path */
            res = d_path(&vma->vm_file->f_path, p_file_path, PATH_MAX);
            if (res == NULL) {
                WHITELIST_ERR("get cur process file path failed.\n");
                goto FAILED_OUT;
            }
            if ((strlen(res) == path_len) && (!(memcmp(res, file_path, path_len)))) {
                goto OUT;
            }
        }
        vma = vma->vm_next;
    }

    error_flag = 1;

FAILED_OUT:
    vma = NULL;
OUT:
    up_read(&mm->mmap_lock);
    mmput(mm);
    if (error_flag) {
        WHITELIST_ERR("match path not found, file_path = %s.\n", file_path);
    }
    vfree(p_file_path);
    p_file_path = NULL;
    return vma;
}

/*
 * read data by vma
 */
STATIC s32 whitelist_read_vma(struct vm_area_struct *vma, char *buf, u32 buf_size, u32 page_index)
{
    s32 ret;
    s32 num;
    char *ptr = NULL;
    struct page *pages = NULL;

    if ((buf_size - page_index * PAGE_SIZE) < PAGE_SIZE) {
        WHITELIST_ERR("buf_size is too short, buf_size[%u], page_index[%u].\n", buf_size, page_index);
        return WL_ERR_PARA;
    }

    num = get_user_pages_fast(vma->vm_start + page_index * PAGE_SIZE, 1, 0, &pages);
    if (num < 0) {
        WHITELIST_ERR("get_user_pages_fast error, num = %d.\n", num);
        return WL_ERR_PARA;
    }

    /* map pages to virtual addr */
    ptr = (char *)kmap(pages);
    if (ptr == NULL) {
        WHITELIST_ERR("phys_to_virt error \n");
        put_page(pages);
        pages = NULL;
        return WL_ERR_MEM_ALLOC;
    }

    ret = memcpy_s((char *)(buf + page_index * PAGE_SIZE), buf_size - page_index * PAGE_SIZE, ptr, PAGE_SIZE);
    if (ret) {
        WHITELIST_ERR("memcpy_s error, ret = %d \n", ret);
    }

    kunmap(pages);
    put_page(pages);
    pages = NULL;
    ptr = NULL;
    return ret;
}

/*
 * get cur file content
 */
STATIC s32 whitelist_calculate_cur_file_hash(struct vm_area_struct *vma, u32 calculate_len, u8 *sha256_code,
    u32 sha256_len)
{
    s32 ret;
    u32 i = 0;
    u32 seg_len;
    u32 nr_pages;
    char *exe_seg = NULL;

    /* get vma length and alloc mem */
    seg_len = vma->vm_end - vma->vm_start;
    nr_pages = seg_len / PAGE_SIZE;
    exe_seg = (char *)vzalloc(seg_len);
    if (exe_seg == NULL) {
        WHITELIST_ERR("vzalloc for executable segment failed.\n");
        return WL_ERR_MEM_ALLOC;
    }

    for (i = 0; i < nr_pages; i++) {
        ret = whitelist_read_vma(vma, exe_seg, seg_len, i);
        if (ret) {
            WHITELIST_ERR("phys_to_virt error, ret = %d.\n", ret);
            goto OUT;
        }
    }

    /* calculate sha256 */
    ret = whitelist_calculate_sha256(exe_seg, calculate_len, sha256_code, sha256_len);
    if (ret) {
        WHITELIST_ERR("whitelist_calculate_sha256 failed, ret = %d\n", ret);
    }

OUT:
    vfree(exe_seg);
    exe_seg = NULL;
    return ret;
}

/*
 * compare process file information, find the same file and check sha256 code
 */
STATIC s32 whitelist_cmp_process_file_info(process_parse_info_t *process_info)
{
    s32 ret;
    u8 sha256_result[WL_SHA256_DIGEST] = {0};
    struct vm_area_struct *vma = NULL;
    struct list_head *n = NULL;
    struct list_head *pos = NULL;
    file_parse_info_t *file_info = NULL;

    if (process_info == NULL) {
        WHITELIST_ERR("process list info is NULL .\n");
        return WL_ERR_PARA;
    }

    if (!list_empty_careful(&process_info->file_list)) {
        list_for_each_safe(pos, n, &process_info->file_list)
        {
            file_info = list_entry(pos, file_parse_info_t, file_list);

            /* get vma by file path */
            vma = whitelist_get_vma_by_file(file_info->file_path, file_info->file_path_len);
            if (vma == NULL) {
                WHITELIST_ERR("whitelist_get_vma_by_file failed.\n");
                return WL_ERR_PARA;
            }

            /* read vma content and calculate sha256 check code */
            ret = whitelist_calculate_cur_file_hash(vma, file_info->act_text_offset, sha256_result, WL_SHA256_DIGEST);
            if (ret) {
                WHITELIST_ERR("whitelist_calculate_cur_file_hash failed, ret = %d.\n", ret);
                return WL_ERR_SHA256_CHECK;
            }

            /* compare sha256 check code and return check result */
            ret = memcmp(file_info->sha256_code, sha256_result, WL_SHA256_DIGEST);
            if (ret) {
                WHITELIST_ERR("compare sha256 code failed, ret = %d.\n", ret);
                return WL_ERR_SHA256_CHECK;
            }
        }
    } else {
        WHITELIST_ERR("process file link list is empty, no compare.\n");
        return WL_ERR_SHA256_CHECK;
    }

    return WL_OK;
}

/*
 * store each process file info to link list
 */
STATIC s32 whitelist_store_each_file_info(file_parse_info_t *file_info)
{
    int ret;
    u32 i = 0;
    u8 byte_val = 0;
    char byte_str[SHA256_BYTE_STR_LEN] = {0};
    char *p_str_byte = byte_str;

    if ((file_info->check_code == NULL) || (file_info->file_path == NULL) || (file_info->text_offset_ascii == NULL)) {
        WHITELIST_ERR("can't get fix file info.\n");
        return WL_ERR_PARA;
    }

    if ((file_info->file_path_len > PATH_MAX) || (file_info->text_offset_len > WL_TEXT_OFFSET_BYTES_MAX)) {
        WHITELIST_ERR("text offset length[0x%x] or path_len[%u] is out of range.\n", file_info->text_offset_len,
            file_info->file_path_len);
        return WL_ERR_FILE_FORMAT;
    }

    /* set segment offset end char ':' to '\0', get .text segment offset */
    file_info->text_offset_ascii[file_info->text_offset_len] = '\0';
    ret = kstrtouint(&file_info->text_offset_ascii[BUF_OFFSET_2BYTES], PROCESS_TEXT_OFFSET_HEX,
        &file_info->act_text_offset);
    if (ret) {
        WHITELIST_ERR("unable to transform input string into text_seg_offset, ret = %d.\n", ret);
        return WL_ERR_SHA256_CHECK;
    }

    /* exchange str to num store to file info link list */
    for (i = 0; i < WL_SHA256_DIGEST; i++) {
        byte_str[0] = file_info->check_code[i * WL_DATA_NUM_EVEN];
        byte_str[1] = file_info->check_code[i * WL_DATA_NUM_EVEN + 1];
        byte_str[WL_DATA_NUM_EVEN] = '\0';
        ret = kstrtou8(p_str_byte, PROCESS_TEXT_OFFSET_HEX, &byte_val);
        if (ret) {
            WHITELIST_ERR("unable to transform input string into byte_val, ret = %d.\n", ret);
            return WL_ERR_SHA256_CHECK;
        }

        file_info->sha256_code[i] = byte_val;
    }

    return WL_OK;
}

/*
 * store process info
 */
STATIC s32 whitelist_store_process_info(process_parse_info_t *process_info)
{
    s32 ret;
    u32 i = 0;
    u32 cur_offset;
    u32 pos_flag = 0;
    u32 already_found;
    file_parse_info_t *file_info = NULL;

    cur_offset = process_info->process_name_len + BUF_OFFSET_2BYTES;
    while (cur_offset < (process_info->content_size - PROCESS_LIST_SHA256_BYTES)) {
        file_info = (file_parse_info_t *)vzalloc(sizeof(file_parse_info_t));
        if (file_info == NULL) {
            WHITELIST_ERR("file info memory alloc failed.\n");
            return WL_ERR_MEM_ALLOC;
        }

        list_add(&file_info->file_list, &process_info->file_list);
        already_found = 0;
        file_info->file_path = &process_info->start_pos[cur_offset];
        for (i = cur_offset; i < process_info->content_size; i++) {
            /* get file path info, then get text segment offset, all end of ':' */
            if ((pos_flag == 0) && (process_info->start_pos[i] == ':')) {
                file_info->file_path_len = i - cur_offset;
                file_info->text_offset_ascii = &process_info->start_pos[i + 1];
                pos_flag = 1;
            } else if ((pos_flag == 1) && ((process_info->start_pos[i] == ':'))) {
                file_info->text_offset_len = i - cur_offset - file_info->file_path_len - 1;
                file_info->check_code = &process_info->start_pos[i + 1];
                already_found = 1;
            }

            if (already_found == 1) {
                cur_offset = i + PROCESS_LIST_SHA256_BYTES + BUF_OFFSET_3BYTES;
                pos_flag = 0;
                break;
            }
        }

        /* store each file info to link list */
        ret = whitelist_store_each_file_info(file_info);
        if (ret) {
            WHITELIST_ERR("whitelist_store_each_file_info failed.\n");
            return WL_ERR_SHA256_CHECK;
        }
    }

    return WL_OK;
}

/*
 * check process info
 */
STATIC s32 whitelist_check_process_info(void)
{
    s32 ret;
    u8 *process_name = NULL;
    u32 process_len = 0;
    u32 already_found = 0;
    struct list_head *n_process = NULL;
    struct list_head *pos_process = NULL;
    process_parse_info_t *process_info = NULL;

    ret = whitelist_get_cur_process_name(&process_name, &process_len);
    if (ret) {
        WHITELIST_ERR("whitelist_get_cur_process_name failed, ret = %d.\n", ret);
        return ret;
    }

    if (!list_empty_careful(&g_process_file.process_list)) {
        list_for_each_safe(pos_process, n_process, &g_process_file.process_list)
        {
            process_info = list_entry(pos_process, process_parse_info_t, process_list);

            /* compare process name and find the process info node */
            ret = memcmp(process_info->process_name, process_name, process_len);
            if (!ret) {
                ret = whitelist_cmp_process_file_info(process_info);
                if (ret) {
                    WHITELIST_ERR("whitelist_cmp_process_file_info failed.\n");
                    return WL_ERR_PARA;
                }

                already_found = 1;
                break;
            }
        }
    } else {
        WHITELIST_ERR("process link list is empty, no content.\n");
        return WL_ERR_PARA;
    }

    if (already_found == 0) {
        WHITELIST_ERR("can't find the same process info.\n");
        return WL_ERR_PARA;
    }

    return WL_OK;
}

/*
 * read /var/process.list file content when ko insmod
 */
s32 whitelist_get_list_file_content(void)
{
    s32 ret = WL_OK;
    loff_t offset = 0;
    ssize_t result;
    struct file *p_file = NULL;
    char *filp_name = PROCESS_LIST_FILE_PATH;

    p_file = filp_open(filp_name, O_RDONLY, 0);
    if ((IS_ERR(p_file)) || (p_file == NULL)) {
        WHITELIST_ERR("filp_open error, err = %ld.\n", PTR_ERR(p_file));
        return WL_ERR_FILE_OPEN;
    }

    /* get file total length */
    if (p_file->f_inode == NULL) {
        WHITELIST_ERR("file inode is NULL.\n");
        ret = WL_ERR_PARA;
        goto OUT;
    }

    g_process_file.file_len = p_file->f_inode->i_size;
    if ((g_process_file.file_len <= SOC_HEAD_SIZE) || (g_process_file.file_len > PROCESS_LIST_FILE_MAX)) {
        WHITELIST_ERR("process list file len out of range: %llu.\n", g_process_file.file_len);
        ret = WL_ERR_PARA;
        goto OUT;
    }

    g_process_file.list_file = (char *)vzalloc(g_process_file.file_len);
    if (g_process_file.list_file == NULL) {
        WHITELIST_ERR("vzalloc file content failed.\n");
        ret = WL_ERR_MEM_ALLOC;
        goto OUT;
    }

    result = kernel_read(p_file, (char *)g_process_file.list_file, g_process_file.file_len, &offset);
    if (result != g_process_file.file_len) {
        WHITELIST_ERR("kernel read file error \n");
        ret = WL_ERR_FILE_READ;
        goto OUT1;
    }

    goto OUT;
OUT1:
    vfree(g_process_file.list_file);
    g_process_file.list_file = NULL;
OUT:
    filp_close(p_file, NULL);
    p_file = NULL;
    return ret;
}

/*
 * parse process list file and store to link list
 */
s32 whitelist_parse_file_info(void)
{
    s32 ret;
    u8 *file_buf = g_process_file.list_file + SOC_HEAD_SIZE;
    u32 file_len = g_process_file.file_len - SOC_HEAD_SIZE;
    struct list_head *n = NULL;
    struct list_head *pos = NULL;
    process_parse_info_t *parse_info = NULL;

    ret = whitelist_get_process_info(file_buf, file_len);
    if (ret) {
        WHITELIST_ERR("get process info fail, ret=%d.\n", ret);
        return WL_ERR_PARA;
    }

    if (list_empty_careful(&g_process_file.process_list)) {
        WHITELIST_ERR("process link list if empty.\n");
        return WL_ERR_PARA;
    }

    list_for_each_safe(pos, n, &g_process_file.process_list)
    {
        parse_info = list_entry(pos, process_parse_info_t, process_list);
        ret = whitelist_store_process_info(parse_info);
        if (ret) {
            WHITELIST_ERR("get process each file info fail, ret=%d.\n", ret);
            return WL_ERR_PARA;
        }

        if (list_empty_careful(&parse_info->file_list)) {
            WHITELIST_ERR("process file link list if empty.\n");
            return WL_ERR_PARA;
        }
    }

    return WL_OK;
}

/*
 * read /var/process.list file content when ko insmod
 */
s32 whitelist_read_process_list_file(void)
{
    s32 ret;

    INIT_LIST_HEAD(&g_process_file.process_list);

    /* get file content */
    ret = whitelist_get_list_file_content();
    if (ret) {
        WHITELIST_ERR("get process list file content fail, ret=%d.\n", ret);
        return ret;
    }

    /* verify soc head */
    ret = whitelist_file_soc_verification(0, g_process_file.list_file, g_process_file.file_len);
    if (ret) {
        WHITELIST_ERR("whitelist_file_soc_verify fail, ret=%d.\n", ret);
        return ret;
    }

    /* parse file info and write info to link list */
    ret = whitelist_parse_file_info();
    if (ret) {
        WHITELIST_ERR("parse file list info fail, ret=%d.\n", ret);
        whitelist_free_process_list_file();
        return ret;
    }

    return WL_OK;
}
EXPORT_SYMBOL(whitelist_read_process_list_file);

/*
 * free process and file link list and process.list file content
 */
void whitelist_free_process_list_file(void)
{
    struct list_head *n_process = NULL;
    struct list_head *pos_process = NULL;
    struct list_head *pos_file = NULL;
    struct list_head *n_file = NULL;
    process_parse_info_t *process_info = NULL;
    file_parse_info_t *file_info = NULL;

    /* free process link list */
    if (!list_empty_careful(&g_process_file.process_list)) {
        list_for_each_safe(pos_process, n_process, &g_process_file.process_list)
        {
            process_info = list_entry(pos_process, process_parse_info_t, process_list);
            /* free file link list */
            if (!list_empty_careful(&process_info->file_list)) {
                list_for_each_safe(pos_file, n_file, &process_info->file_list)
                {
                    file_info = list_entry(pos_file, file_parse_info_t, file_list);
                    list_del(&file_info->file_list);
                    vfree(file_info);
                    file_info = NULL;
                }
            }

            list_del(&process_info->process_list);
            vfree(process_info);
            process_info = NULL;
        }
    }

    /* free file content */
    if (g_process_file.list_file != NULL) {
        vfree(g_process_file.list_file);
        g_process_file.list_file = NULL;
    }
}
EXPORT_SYMBOL(whitelist_free_process_list_file);

/*
 * ioctl whitelist process
 */
s32 whitelist_process_handler(const char **list_name, u32 name_num)
{
    s32 ret;
    s32 i = 0;

    if ((list_name == NULL) || (name_num > WL_NAME_NUM_MAX)) {
        WHITELIST_ERR("list name para is err, name_num: %u.\n", name_num);
        return WL_ERR_PARA;
    }

    for (i = 0; i < name_num; i++) {
        if (list_name[i] == NULL) {
            WHITELIST_ERR("input process name[%d] is NULL.\n", i);
            return WL_ERR_PARA;
        }
    }

    /* check cur process name */
    ret = whitelist_check_process_name(list_name, name_num);
    if (ret) {
        WHITELIST_ERR("whitelist_check_process_name failed, ret = %d.\n", ret);
        return ret;
    }

    /* check process file list and cur process info, check if the checkcode is the same */
    ret = whitelist_check_process_info();
    if (ret) {
        WHITELIST_ERR("whitelist_check_process_info failed, ret = %d.\n", ret);
    }

    return ret;
}
EXPORT_SYMBOL(whitelist_process_handler);
